home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Fun, Tricks & Hacks / a friend / a friend.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-24  |  7.9 KB  |  383 lines  |  [TEXT/KAHL]

  1. /*
  2.  * a friend.c
  3.  *
  4.  * by Alex Kourakos
  5.  * Copyright © 1993 by Sokaruok/Xela
  6.  *
  7.  * Written in THINK C v4.0 (I’m still waiting for 6.0)
  8.  *
  9.  * r930623
  10.  * v1.3.0b0
  11.  *   Does a few checks to make sure it can run on this computer. Also,
  12.  *   recompiled with 68000 code only. Also, if it can’t install itself,
  13.  *   it beeps and doesn’t take up system heap space. Version is now Beta.
  14.  * v1.2.0d0
  15.  *   Rewrote to get preferences from resource fork and not hard-wired
  16.  *   into program.
  17.  * v1.1.2d1
  18.  *   Removed references to “mistakes.” I don’t think it’s worth the
  19.  *   trouble to make our friend a bad typist.
  20.  * v1.1.2d0
  21.  *   Fixed a bug that would only allow exactly 8 strings in the resource
  22.  *   fork.
  23.  * v1.1.1d0
  24.  *   Fixed a big bug in the way times were selected.
  25.  * v1.1.0d0
  26.  *   Wrote my own random number routine, because the QuickDraw one stinks
  27.  *   from an INIT.
  28.  * v1.0.1d0
  29.  *   Added sounds with each keypress.
  30.  * v1.0.0d0
  31.  *   Rewrote to get strings from resources. Made a lot of other changes.
  32.  * v0.1.0d0
  33.  *   Now randomly adjusts time between keystrokes and between cycles. Added
  34.  *   second message.
  35.  * v0.0.0d0
  36.  *   This program wants to be your friend.
  37.  */
  38.  
  39. #include <MacTypes.h>
  40. #include    <QuickDraw.h>
  41. #include    <OSUtil.h>
  42. #include    <EventMgr.h>
  43. #include    <ResourceMgr.h>
  44. #include    <SoundMgr.h>
  45. #include    <pascal.h>
  46. #include    <SetUpA4.h>
  47.  
  48.  
  49. /*
  50.  * Program constants.
  51.  */
  52.  
  53. #define    kBaseResID                128
  54. #define    kAsynchronousSound    false
  55. #define    _SystemTask                0xa9b4
  56.  
  57. #define    kPrefsType                'PREF'
  58.  
  59. #define    kMaxStr                    16
  60.  
  61. #define    kSpaceKeyCode            0x20
  62. #define    kReturnKeyCode            0x0d
  63.  
  64. /*
  65.  * Resources.
  66.  */
  67.  
  68. #define    rTheStrings                kBaseResID
  69. #define    rAnyKeySound            kBaseResID
  70. #define    rSpaceKeySound            (kBaseResID+1)
  71. #define    rReturnKeySound        (kBaseResID+2)
  72. #define    rPrefs                    kBaseResID
  73.  
  74.  
  75. /*
  76.  * My types.
  77.  */
  78.  
  79. typedef struct {
  80.     unsigned long    initialDelay,maxTime,maxTypingTime;
  81.     } **FriendPrefsHandle;
  82.  
  83. typedef void (*SystemTaskPtr)(void);
  84.  
  85.  
  86. /*
  87.  * Function prototypes.
  88.  */
  89.  
  90. void    main(void);
  91. void    MySystemTask(void);
  92. int    RandomInt(int);
  93. void    TypeChar(char);
  94.  
  95.  
  96. /*
  97.  * Global variables.
  98.  */
  99.  
  100. SystemTaskPtr        gOldSystemTask;
  101. unsigned long        gLastTickCount,gRandomSeed,gTime;
  102. Str255                gStrings[kMaxStr];
  103. int                    gWhichString,gCharPos;
  104. short                    gNumStr;
  105. Handle                gAnyKeySound,gSpaceKeySound,gReturnKeySound;
  106. FriendPrefsHandle    gPrefs;
  107.  
  108.  
  109. /*
  110.  * Main. This is called first in the INIT code, thanks to THINK’s glue, and
  111.  * it installs everything nicely.
  112.  */
  113.  
  114. void    main(void) {
  115.     Ptr            whereWeLive;
  116.     int            strCounter;
  117.     SysEnvRec    theMac;
  118.  
  119.     /*
  120.      * THINK C is kind enough to stuff our address into A0.
  121.      */
  122.  
  123.     RememberA0();
  124.  
  125.     /* 
  126.      * THINK C is also nice enough to let us access our globals from
  127.      * A4, since A5 is probably “in use.”
  128.      */
  129.  
  130.     SetUpA4();
  131.  
  132.     /*
  133.      * Stuff the address (in register A0) into whereWeLive.
  134.      */
  135.  
  136.     asm { move.l    A0,whereWeLive    }
  137.  
  138.     /*
  139.      * Before we do anything, let’s make sure we’re running on a decent
  140.      * Macintosh. I don’t know any other way to check for the Sound Manager
  141.      * at this point.
  142.      */
  143.  
  144.     SysEnvirons(curSysEnvVers,&theMac);
  145.  
  146.     /*
  147.      * I want at least System 6.
  148.      */
  149.  
  150.     if( theMac.systemVersion < 0x0600 ) {
  151.         SysBeep(666);
  152.         goto done;
  153.         }
  154.  
  155.     /*
  156.      * Get the preferences. They are marked as non-purgeable system heap resources,
  157.      * but the Mac will forget about them if we don’t detach them from this file,
  158.      * which will be closed as soon as the INIT finished loading.
  159.      */
  160.  
  161.     gPrefs = (FriendPrefsHandle)GetResource(kPrefsType,rPrefs);
  162.  
  163.     if( gPrefs == nil ) {
  164.         SysBeep(666);
  165.         goto done;
  166.         }
  167.  
  168.     DetachResource(gPrefs);
  169.  
  170.     /*
  171.      * Initialize the global variables.
  172.      */
  173.  
  174.     gLastTickCount = TickCount();
  175.     gTime = ((*gPrefs)->initialDelay);
  176.     gWhichString = 0;
  177.     gCharPos = 1;
  178.     gRandomSeed = TickCount() + (unsigned long)whereWeLive;
  179.     gNumStr = **((short **)GetResource('STR#',rTheStrings));
  180.     if( gNumStr > kMaxStr )
  181.         gNumStr = kMaxStr;
  182.     
  183.     /*
  184.      * Let’s snag all the strings out of our resource fork.
  185.      */
  186.  
  187.     for( strCounter = 0 ; strCounter < gNumStr ; ++strCounter )
  188.         GetIndString(gStrings[strCounter],rTheStrings,strCounter + 1);
  189.  
  190.     /*
  191.      * Let’s get all the sounds. They too are marked as nonpurgeable
  192.      * system resources and will be detached.
  193.      */
  194.  
  195.     gAnyKeySound = GetResource(soundListRsrc,rAnyKeySound);
  196.     gSpaceKeySound = GetResource(soundListRsrc,rSpaceKeySound);
  197.     gReturnKeySound = GetResource(soundListRsrc,rReturnKeySound);
  198.  
  199.     if( (gAnyKeySound == nil) ||
  200.         (gSpaceKeySound == nil) || 
  201.         (gReturnKeySound == nil) ) {
  202.         SysBeep(666);
  203.         goto done;
  204.         }
  205.  
  206.     DetachResource(gAnyKeySound);
  207.     DetachResource(gSpaceKeySound);
  208.     DetachResource(gReturnKeySound);
  209.  
  210.     /*
  211.      * If we got this far, everything’s okay. Let’s detach ourselves in the
  212.      * system heap.
  213.      */
  214.  
  215.     DetachResource(RecoverHandle(whereWeLive));
  216.  
  217.     /*
  218.      * Save the original SystemTask address, and then replace it with our own
  219.      * (evil laugh.) Note the StripAddress function to ensure this is 32-bit
  220.      * clean.
  221.      */
  222.  
  223.     gOldSystemTask = (SystemTaskPtr)StripAddress(NGetTrapAddress(_SystemTask,ToolTrap));
  224.     NSetTrapAddress((long)StripAddress(MySystemTask),_SystemTask,ToolTrap);
  225.  
  226.     /*
  227.      * Make A4 whatever it was again.
  228.      */
  229.  
  230. done:
  231.     RestoreA4();
  232.     }
  233.  
  234.  
  235. /*
  236.  * This is the function that gets called instead of SystemTask. Notice that it
  237.  * is a head patch.
  238.  */
  239.  
  240. void MySystemTask(void) {
  241.     SystemTaskPtr    OldSystemTask;
  242.     
  243.     /*
  244.      * We need to get to our globals.
  245.      */
  246.  
  247.     SetUpA4();
  248.  
  249.     /*
  250.      * Save the address in a local variable, so that it can be called from
  251.      * within the normal stack frame.
  252.      */
  253.  
  254.     OldSystemTask = gOldSystemTask;
  255.  
  256.     /*
  257.      * If we have exceeded our time limit, then do stuff, otherwise, don’t
  258.      * do anything.
  259.      */
  260.  
  261.     if( TickCount() >= (gLastTickCount + gTime) ) {
  262.  
  263.         /*
  264.          * Shove the next character in to the event queue.
  265.          */
  266.  
  267.         TypeChar(gStrings[gWhichString][++gCharPos]);
  268.  
  269.         /*
  270.          * Check to see if we have just typed the last character of the
  271.          * string (the first character in a Pascal string is the length.)
  272.          */
  273.  
  274.         if( gCharPos == gStrings[gWhichString][0] ) {
  275.     
  276.             /*
  277.              * If so, choose a new string, and get a random time, and reset
  278.              * the character position to the first character.
  279.              */
  280.     
  281.             gWhichString = RandomInt(gNumStr);
  282.             gTime = ((*gPrefs)->maxTime) - RandomInt((int)((*gPrefs)->maxTime) >> 2);
  283.             gCharPos = 0;
  284.             }
  285.         else {
  286.             
  287.             /*
  288.              * Otherwise, choose a short time for the next keystroke. If the next
  289.              * character is the same as this one, choose a shorter time (to simulate
  290.              * faster typing of one key over and over). This looks like a mess, but
  291.              * it works.
  292.              */
  293.         
  294.             gTime = (gStrings[gWhichString][gCharPos] == gStrings[gWhichString][gCharPos + 1]) ?
  295.                     (long)RandomInt((int)((*gPrefs)->maxTypingTime) >> 1) :
  296.                     (((*gPrefs)->maxTypingTime) - (long)RandomInt((int)((*gPrefs)->maxTypingTime)));
  297.             }
  298.  
  299.         /*
  300.          * If something happened, let’s update the last time.
  301.          */
  302.  
  303.         gLastTickCount = TickCount();
  304.         }
  305.  
  306.     /*
  307.      * Restore normal global pointers.
  308.      */
  309.  
  310.     RestoreA4();
  311.  
  312.     /* 
  313.      * Call the old SystemTask.
  314.      */
  315.  
  316.     OldSystemTask();
  317.     }
  318.  
  319.  
  320. /*
  321.  * Returns a random number between 0 and max - 1.
  322.  */
  323.  
  324. int RandomInt(int max) {
  325.     int                returnMe;
  326.     unsigned long    a,b;
  327.  
  328.     /*
  329.      * This is from _C_Mathematical_Function_Handbook_ by Baker.
  330.      */
  331.  
  332.     a = gRandomSeed / 127773L;
  333.     b = gRandomSeed % 127773L;
  334.     gRandomSeed = (16807 * b) - (2836 * a);
  335.  
  336.     returnMe = (int)(gRandomSeed) % max;
  337.  
  338.     /*
  339.      * If it is negative, we want it positive.
  340.      */
  341.  
  342.     if( returnMe < 0 )
  343.         return -returnMe;
  344.     return returnMe;
  345.     }
  346.  
  347.  
  348. /*
  349.  * Places the character into the Event Queue, and then plays the
  350.  * appropriate sound.
  351.  */
  352.  
  353. void TypeChar(char c) {
  354.     Handle    theSound;
  355.  
  356.     switch( c ) {
  357.         case kSpaceKeyCode:
  358.             theSound = gSpaceKeySound;
  359.             break;
  360.             
  361.         case kReturnKeyCode:
  362.             theSound = gReturnKeySound;
  363.             break;
  364.         
  365.         default:
  366.             theSound = gAnyKeySound;
  367.             break;
  368.         }
  369.  
  370.     /* 
  371.      * Places an event of type keyDown, with a message of this character. All the
  372.      * other fields of the event record are set appropriately by the Event Manager.
  373.      */
  374.  
  375.     PostEvent(keyDown,(long)(c));
  376.  
  377.     /*
  378.      * Use the Sound Manager to play the sound.
  379.      */
  380.  
  381.     SndPlay(nil,theSound,kAsynchronousSound);
  382.     }
  383.